[新ツール] AWS Serverless Application Model (AWS SAM) を使ってサーバーレスアプリケーションを構築する
AWS Serverless Application Model (AWS SAM) とは
AWS Serverless Application Model (以降 AWS SAM) は、AWS が公式で提供しているサーバーレスアプリケーションを構築するためのフレームワーク (モデル) です。Lambda, API Gateway, DynamoDB のリソースをひとまとめに管理 (作成 / 更新 / 削除) することができます。
AWS SAM は、元々 Flourish という名前で知られていたものです。今年5月に開催された Serverless Conf にて Tim Wagner から発表されて以来、期待が集まっていました。re:Invent 2016 を目前に、ついにリリースされました。
本記事では AWS SAM がどういったものなのか ざっくり調べてまとめてみました。
AWS SAM は AWS CloudFormation の拡張である
AWS SAM は言うならば、 AWS CloudFormation (以降 CFn) のサーバーレス拡張 です。AWS SAM の各リソースは CFn のこれまでのリソース定義と似たフォーマットで定義されており、このフォーマットで記述されたテンプレートを CFn に渡すことで、スタックとして AWS 上に展開することができます。具体的には以下のような感じです。YAML または JSON で記述することができます。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Resources: FunctionName: Type: AWS::Serverless::Function Properties: Handler: index.handler Runtime: nodejs4.3
Transform: AWS::Serverless-2016-10-31
というところがミソで、これを書くことで CFn はテンプレートを AWS SAM フォーマットのテンプレートとして読むようです。
AWS SAM でサーバーレスアプリケーションを構築してみよう
本記事では、GitHub で公開されているサンプルの api_backend を参考に構築してみたいと思います。こちらは、DynamoDB テーブルの CRUD を API Gateway + Lambda で構築するといったものです。
作業用ディレクトリの作成
まずは AWS SAM で取り扱うファイルをまとめるため、ローカルの適当な場所にディレクトリを作成しておきましょう。
$ mkdir api_backend $ cd api_backend
以降、このディレクトリ配下で作業します。
Lambda ファンクション
まずは DynamoDB テーブルの CRUD を提供する Lambda ファンクションを用意します *1。dynamodb-doc を使って DynamoDB の API を呼び出しています。また、先日 AWS SAM と同時にリリースされた Lambda の環境変数を使って、DynamoDB のテーブル名を取得するようにしています。
'use strict'; console.log('Loading function'); let doc = require('dynamodb-doc'); let dynamo = new doc.DynamoDB(); const tableName = process.env.TABLE_NAME; const createResponse = (statusCode, body) => { return { "statusCode": statusCode, "body": body } }; exports.get = (event, context, callback) => { var params = { "TableName": tableName, "Key": { "id": event.pathParameters.resourceId } }; dynamo.getItem(params, (err, data) => { var response; if (err) response = createResponse(500, err); else response = createResponse(200, data.Item.doc); callback(null, response); }); }; exports.put = (event, context, callback) => { var item = { "id": event.pathParameters.resourceId, "doc": event.body }; var params = { "TableName": tableName, "Item": item }; dynamo.putItem(params, (err, data) => { var response; if (err) response = createResponse(500, err); else response = createResponse(200, null); callback(null, response); }); }; exports.delete = (event, context, callback) => { var params = { "TableName": tableName, "Key": { "id": event.pathParameters.resourceId } }; dynamo.deleteItem(params, (err, data) => { var response; if (err) response = createResponse(500, err); else response = createResponse(200, null); callback(null, response); }); };
こちらの Lambda ファンクションを index.js
という名前で保存し、app.zip
という名前の Zip ファイルに圧縮しておきます。なお、これらのファイル名は何でも構いません。
$ zip app.zip index.js -x "*.DS_Store"
SAM テンプレートの作成
次に、SAM 形式のテンプレートを作成しましょう。
AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Simple CRUD webservice. State is stored in a SimpleTable (DynamoDB) resource. Resources: GetFunction: Type: AWS::Serverless::Function Properties: Handler: index.get Runtime: nodejs4.3 Policies: AmazonDynamoDBReadOnlyAccess Environment: Variables: TABLE_NAME: !Ref Table Events: GetResource: Type: Api Properties: Path: /resource/{resourceId} Method: get PutFunction: Type: AWS::Serverless::Function Properties: Handler: index.put Runtime: nodejs4.3 Policies: AmazonDynamoDBFullAccess Environment: Variables: TABLE_NAME: !Ref Table Events: PutResource: Type: Api Properties: Path: /resource/{resourceId} Method: put DeleteFunction: Type: AWS::Serverless::Function Properties: Handler: index.delete Runtime: nodejs4.3 Policies: AmazonDynamoDBFullAccess Environment: Variables: TABLE_NAME: !Ref Table Events: DeleteResource: Type: Api Properties: Path: /resource/{resourceId} Method: delete Table: Type: AWS::Serverless::SimpleTable
`` と書かれている部分は、Lambda ファンクションの Zip ファイルのアップロード先となる S3 バケットの名前に変更してください。S3 バケットを作成するには、次のようなコマンドを叩きます。
$ aws s3 mb s3://<bucket-name> --region <region>
パッケージ & デプロイ
次に、パッケージとデプロイを行います。ここからは AWS CLI を使うことが必須となるので、念のため最新バージョンにしておきましょう。
$ aws --version aws-cli/1.11.19 Python/2.7.11 Darwin/16.3.0 botocore/1.4.76
パッケージ
パッケージは、Lambda ファンクションの Zip ファイルを S3 バケットにアップロードすることを指します。AWS CLI の CloudFormation の package コマンド を叩くだけで、コマンドを実行したディレクトリにある Zip ファイルをアップロードすることができます。
$ aws cloudformation package \ --template-file template.yaml \ --output-template-file serverless-output.yaml \ --s3-bucket <bucket-name> Uploading to d922f3e6e1268d5923d0746c1996de62 2482 / 2482.0 (100.00%) Successfully packaged artifacts and wrote output template to file serverless-output.yaml. Execute the following command to deploy the packaged template aws cloudformation deploy --template-file /Users/User/work/api_backend/serverless-output.yaml --stack-name <YOUR STACK NAME>
S3 に Zip ファイルがアップロードされ、そのファイルの Uri が --template-file
で指定したテンプレートに書き足され、--output-template-file
で指定したファイル名で出力されます。抜粋すると、以下のようになっています。
AWSTemplateFormatVersion: '2010-09-09' Description: Simple CRUD webservice. State is stored in a SimpleTable (DynamoDB) resource. Resources: DeleteFunction: Properties: CodeUri: s3://<bucket-name>/d922f3e6e1268d5923d0746c1996de62 Environment: Variables: TABLE_NAME: Ref: Table Events: DeleteResource: Properties: Method: delete Path: /resource/{resourceId} Type: Api Handler: index.delete Policies: AmazonDynamoDBFullAccess Runtime: nodejs4.3 Type: AWS::Serverless::Function
デプロイ
パッケージで書き出したテンプレートファイルを元に、AWS CLI の CloudFormation の deploy コマンド を叩きます。これで、テンプレートに記述されている通りのリソースが構築されます。
$ aws cloudformation deploy \ --template-file serverless-output.yaml \ --stack-name <new-stack-name> \ --capabilities CAPABILITY_IAM Waiting for changeset to be created.. Waiting for stack create/update to complete Successfully created/updated stack - api-backend
`` には、適当なスタック名を付けてください。
処理が完了すると、各リソースが構築されていることが確認できます。
CFn Stack が完了しており、
DynamoDB がいい感じで構築されており、
Lambda がいい感じで構築されており、
API Gateway がいい感じで構築されています。
試す
まずは PUT から。DynamoDB を考慮し、Upsert を行うような API になっています。
$ curl -H "Accept: application/json" -H "Content-type: application/json" \ -X PUT https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/resource/hoge \ -d '{"name":"hoge", "description":"I am hoge."}'
上記で作成したものを GET します。
$ curl -H "Content-type: application/json" \ -X GET https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/resource/hoge {"name":"hoge", "description":"I am hoge."}
最後に DELETE で消してみます。
$ curl -H "Content-type: application/json" \ -X DELETE https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/resource/hoge
いい感じに動作しています! DynamoDB のマネジメントコンソールで、アイテムの変化を確認しながら試すと良いでしょう。
まとめ
AWS SAM についてざっくりと触れてみました。サーバーレスアプリケーションは適用可能範囲が多岐に渡るので、用途によってテンプレートの構成は変わってきます。今後は AWS SAM を使ってサーバーレスアプリケーションを構築する方法を、用途別にご紹介できればと思います。
参考
- awslabs/serverless-application-model (GitHub)
- AWS Serverless Application Model (SAM) - awslabs/serverless-application-model
- Deploying Lambda-based Applications - AWS Lambda
- Create Your Own Serverless Application - AWS Lambda
- Introducing Simplified Serverless Application Deployment and Management | AWS Compute Blog
脚注
- 筆者が試した時点では、このソースコードの20行目の「"id"」の部分が GitHub のサンプルコードでは「id」となっており GET でエラーが発生します。ご留意を。 ↩